package lucene.search
{
	
	import flash.errors.IllegalOperationError;
	import flash.utils.describeType;
	import flash.utils.getQualifiedClassName;
	
	public class Similarity
	{
		
		private static var _defaultImplementation:Similarity;
			
	    /**
	     * Cache of decoded bytes.
	     * Array of floats
	     *
	     * @var array
	     */
	    private static var _normTable:Array = [0.0,
	                                    	5.820766E-10,
	                                        6.9849193E-10,
	                                        8.1490725E-10,
	                                        9.313226E-10,
	                                        1.1641532E-9,
	                                        1.3969839E-9,
	                                        1.6298145E-9,
	                                        1.8626451E-9,
	                                        2.3283064E-9,
	                                        2.7939677E-9,
	                                        3.259629E-9,
	                                        3.7252903E-9,
	                                        4.656613E-9,
	                                        5.5879354E-9,
	                                        6.519258E-9,
	                                        7.4505806E-9,
	                                        9.313226E-9,
	                                        1.1175871E-8,
	                                        1.3038516E-8,
	                                        1.4901161E-8,
	                                        1.8626451E-8,
	                                        2.2351742E-8,
	                                        2.6077032E-8,
	                                        2.9802322E-8,
	                                        3.7252903E-8,
	                                        4.4703484E-8,
	                                        5.2154064E-8,
	                                        5.9604645E-8,
	                                        7.4505806E-8,
	                                        8.940697E-8,
	                                        1.0430813E-7,
	                                        1.1920929E-7,
	                                        1.4901161E-7,
	                                        1.7881393E-7,
	                                        2.0861626E-7,
	                                        2.3841858E-7,
	                                        2.9802322E-7,
	                                        3.5762787E-7,
	                                        4.172325E-7,
	                                        4.7683716E-7,
	                                        5.9604645E-7,
	                                        7.1525574E-7,
	                                        8.34465E-7,
	                                        9.536743E-7,
	                                        1.1920929E-6,
	                                        1.4305115E-6,
	                                        1.66893E-6,
	                                        1.9073486E-6,
	                                        2.3841858E-6,
	                                        2.861023E-6,
	                                        3.33786E-6,
	                                        3.8146973E-6,
	                                        4.7683716E-6,
	                                        5.722046E-6,
	                                        6.67572E-6,
	                                        7.6293945E-6,
	                                        9.536743E-6,
	                                        1.1444092E-5,
	                                        1.335144E-5,
	                                        1.5258789E-5,
	                                        1.9073486E-5,
	                                        2.2888184E-5,
	                                        2.670288E-5,
	                                        3.0517578E-5,
	                                        3.8146973E-5,
	                                        4.5776367E-5,
	                                        5.340576E-5,
	                                        6.1035156E-5,
	                                        7.6293945E-5,
	                                        9.1552734E-5,
	                                        1.0681152E-4,
	                                        1.2207031E-4,
	                                        1.5258789E-4,
	                                        1.8310547E-4,
	                                        2.1362305E-4,
	                                        2.4414062E-4,
	                                        3.0517578E-4,
	                                        3.6621094E-4,
	                                        4.272461E-4,
	                                        4.8828125E-4,
	                                        6.1035156E-4,
	                                        7.324219E-4,
	                                        8.544922E-4,
	                                        9.765625E-4,
	                                        0.0012207031,
	                                        0.0014648438,
	                                        0.0017089844,
	                                        0.001953125,
	                                        0.0024414062,
	                                        0.0029296875,
	                                        0.0034179688,
	                                        0.00390625,
	                                        0.0048828125,
	                                        0.005859375,
	                                        0.0068359375,
	                                        0.0078125,
	                                        0.009765625,
	                                        0.01171875,
	                                        0.013671875,
	                                        0.015625,
	                                        0.01953125,
	                                        0.0234375,
	                                        0.02734375,
	                                        0.03125,
	                                        0.0390625,
	                                        0.046875,
	                                        0.0546875,
	                                        0.0625,
	                                        0.078125,
	                                        0.09375,
	                                        0.109375,
	                                        0.125,
	                                        0.15625,
	                                        0.1875,
	                                        0.21875,
	                                        0.25,
	                                        0.3125,
	                                        0.375,
	                                        0.4375,
	                                        0.5,
	                                        0.625,
	                                        0.75,
	                                        0.875,
	                                        1.0,
	                                        1.25,
	                                        1.5,
	                                        1.75,
	                                        2.0,
	                                        2.5,
	                                        3.0,
	                                        3.5,
	                                        4.0,
	                                        5.0,
	                                        6.0,
	                                        7.0,
	                                        8.0,
	                                        10.0,
	                                        12.0,
	                                        14.0,
	                                        16.0,
	                                        20.0,
	                                        24.0,
	                                        28.0,
	                                        32.0,
	                                        40.0,
	                                        48.0,
	                                        56.0,
	                                        64.0,
	                                        80.0,
	                                        96.0,
	                                        112.0,
	                                        128.0,
	                                        160.0,
	                                        192.0,
	                                        224.0,
	                                        256.0,
	                                        320.0,
	                                        384.0,
	                                        448.0,
	                                        512.0,
	                                        640.0,
	                                        768.0,
	                                        896.0,
	                                        1024.0,
	                                        1280.0,
	                                        1536.0,
	                                        1792.0,
	                                        2048.0,
	                                        2560.0,
	                                        3072.0,
	                                        3584.0,
	                                        4096.0,
	                                        5120.0,
	                                        6144.0,
	                                        7168.0,
	                                        8192.0,
	                                        10240.0,
	                                        12288.0,
	                                        14336.0,
	                                        16384.0,
	                                        20480.0,
	                                        24576.0,
	                                        28672.0,
	                                        32768.0,
	                                        40960.0,
	                                        49152.0,
	                                        57344.0,
	                                        65536.0,
	                                        81920.0,
	                                        98304.0,
	                                        114688.0,
	                                        131072.0,
	                                        163840.0,
	                                        196608.0,
	                                        229376.0,
	                                        262144.0,
	                                        327680.0,
	                                        393216.0,
	                                        458752.0,
	                                        524288.0,
	                                        655360.0,
	                                        786432.0,
	                                        917504.0,
	                                        1048576.0,
	                                        1310720.0,
	                                        1572864.0,
	                                        1835008.0,
	                                        2097152.0,
	                                        2621440.0,
	                                        3145728.0,
	                                        3670016.0,
	                                        4194304.0,
	                                        5242880.0,
	                                        6291456.0,
	                                        7340032.0,
	                                        8388608.0,
	                                        1.048576E7,
	                                        1.2582912E7,
	                                        1.4680064E7,
	                                        1.6777216E7,
	                                        2.097152E7,
	                                        2.5165824E7,
	                                        2.9360128E7,
	                                        3.3554432E7,
	                                        4.194304E7,
	                                        5.0331648E7,
	                                        5.8720256E7,
	                                        6.7108864E7,
	                                        8.388608E7,
	                                        1.00663296E8,
	                                        1.17440512E8,
	                                        1.34217728E8,
	                                        1.6777216E8,
	                                        2.01326592E8,
	                                        2.34881024E8,
	                                        2.68435456E8,
	                                        3.3554432E8,
	                                        4.02653184E8,
	                                        4.69762048E8,
	                                        5.3687091E8,
	                                        6.7108864E8,
	                                        8.0530637E8,
	                                        9.395241E8,
	                                        1.07374182E9,
	                                        1.34217728E9,
	                                        1.61061274E9,
	                                        1.87904819E9,
	                                        2.14748365E9,
	                                        2.68435456E9,
	                                        3.22122547E9,
	                                        3.75809638E9,
	                                        4.2949673E9,
	                                        5.3687091E9,
	                                        6.4424509E9,
	                                        7.5161928E9 ];
		
		public function Similarity(self:Similarity)
		{
			if (self != this) {
				throw new IllegalOperationError("Can't instantiate an abstract class directly")
			}
			
			//these functions MUST be implemented in subclasses
			var unimplemented:Array = [queryNorm, lengthNorm, tf, sloppyFreq, idfFreq, coord];
			//get the fully-qualified name the abstract class
			var abstractTypeName:String = getQualifiedClassName(Similarity);
			//get a list of all the methods declared by the abstract class
			//if a subclass overrides a function, declaredBy will contain the subclass name
			var selfDescription:XML = describeType(this);
			var methods:XMLList = selfDescription.method.(@declaredBy == abstractTypeName && unimplemented.indexOf(this[@name])>= 0);
			if(methods.length()> 0)
			{
				//we'll only get here if the function is still unimplemented
				var concreteTypeName:String = getQualifiedClassName(this);
				throw new IllegalOperationError("Function " + methods[0].@name + " from abstract class " + abstractTypeName + " has not been implemented by subclass " + concreteTypeName);
			}			
		}

		public static function setDefault():void
		{
			/**
			 * @todo to implement
			 */
		} 

		public static function getDefault():Similarity
		{
			if (Similarity._defaultImplementation == null) {
				Similarity._defaultImplementation = new DefaultSimilarity();
			}
			
			return Similarity._defaultImplementation;
		}

		public static function decodeNorm(byte:int):Number
		{
			return Similarity._normTable[byte & 0xFF];
		} 
		
		public static function encodeNorm(f:Number):int
		{
			return Similarity._floatToByte(f);
		}

		private static function _floatToByte(f:Number):int
		{
	        // round negatives up to zero
	        if (f <= 0.0) {
	            return 0;
	        }
	
	        // search for appropriate value
	        var lowIndex:int = 0;
	        var highIndex:int = 255;
	        while (highIndex >= lowIndex) {
	            // mid = (highIndex - lowIndex)/2;
	            var mid:int = (highIndex + lowIndex) >> 1;
	            var delta:int = f - Similarity._normTable[mid];
	
	            if (delta < 0) {
	                highIndex = mid-1;
	            } else if (delta > 0) {
	                lowIndex  = mid+1;
	            } else {
	                return mid; // We got it!
	            }
	        }
	
	        // round to closest value
	        if (highIndex != 255 &&
	            f - Similarity._normTable[highIndex] > Similarity._normTable[highIndex+1] - f ) {
	            return highIndex + 1;
	        } else {
	            return highIndex;
	        }		
		}
		
		public function idf(aTerm:Array, reader:IReader):*
		{
			var _idf:Number = 0.0;
			for each (var term:* in aTerm) {
				_idf += this.idfFreq(reader.docFreq(term), reader.count());
			}
			return _idf;
		}
		
		/**
		 * abstract
		 */
		public function lengthNorm(fieldName:String, numTerms:int):Number {return 0;}

		/**
		 * abstract
		 */		
		public function tf(freq:Number):Number {return 0};
		
		/**
		 * abstract
		 */		
		public function sloppyFreq(distance:int):Number {return 0};

		/**
		 * abstract
		 */		
		public function idfFreq(docFreq:int, numDocs:int):Number {return 0};

		/**
		 * abstract
		 */		
		public function coord(overlap:int, maxOverlap:int):Number {return 0};

		/**
		 * abstract
		 */		
		public function queryNorm(sum:Number):Number {return 0};

	}
}